一个 META 标签造成百度统计无法使用

阅读量:3487 字数:5224 阅读时间:13min

缘起

本站从搭建以来至今一直使用百度统计作为流量分析工具,但后台显示从来没有访客。一直以为是本站知名度过低造成确实没有访客,但也不至于不把我自己算上吧?而且百度统计后台一直提示我“代码安装不正确”。按照官方文档进行故障排查仍没有找到原因。放弃。

直到今天我才找到原因:因为这一行代码,造成百度统计不能正常工作。

<meta name="referrer" content="no-referrer">

HTTP 请求头中 Referer 的含义和作用

什么是 Referer

Referer 是 HTTP 请求头 Request Header 中的一部分。当浏览器向 web 服务器发送请求的时候,请求头信息里有包含 Referer。比如我在 https://marksanders.cn/friends/ 里有一个 https://triplan.tech/ 的链接,那么点击该链接 ,HTTP 请求头的信息里就有:

Referer=https://marksanders.cn/friends/

由此可以看出来,它表示当前请求的来源。被请求方可以读取出 Referer 从而知道该请求的来源。

Referer 请求示例图

拼写问题

Referer 的正确英语拼法是 referrer。由于早期 HTTP 规范的拼写错误,为了保持向后兼容就将错就错了。其它网络技术的规范企图修正此问题,使用正确拼法,所以目前拼法不统一。还有它第一个字母是大写。

Referer 的作用

防盗链

图片服务器在每次请求时判断 Referer 是否在白名单中,如果是则继续访问,不是则拦截当前请求(通常返回错误码 403)。这样我网站上的图片资源仅允许在本网站使用,若被引用至其它网站中则图片不能正常显示。图为在其它域下无法正常显示豆瓣网的图片。

无法正常显示设置防盗链的图片

防止恶意请求

某网站有一个内部接口用于获取一些数据,现在只允许这个接口在本站调用。为防止被恶意请求,可以让该接口判断 Referer 的值是否为本站,极大增加了接口的安全性、私密性。

Referer 的不可靠性

Referer 不但可用于上述防盗链、防止恶意请求上,还可以根据需求用于其他用途。当然,Referer 只是一个参考,并不能完全依赖于它。

比方说,如下 python 代码则实现了如何伪造 HTTP 请求头中的 Referer 值:

#!/usr/bin/python
import urllib2
import sys
  
url = 'https://triplan.tech/'
  
req = urllib2.Request(url)
req.add_header('Referer','//marksanders.cn/')  # 伪造 Referer
r = urllib2.urlopen(req)
  
html = r.read()
receive_header = r.info()

html = html.decode('utf-8').encode(sys.getfilesystemencoding())

print html

百度统计与 Referer

百度统计正是通过获取 Referer 值从而知道当前请求的页面。

referrer 策略与 meta 标签

referrer 的取值

1、no-referrer:所有请求不发送 referrer。

2、no-referrer-when-downgrade(默认值):当请求安全级别下降时不发送 referrer。目前,只有一种情况会发生安全级别下降,即从 HTTPS 到 HTTP。HTTPS 到 HTTP 的资源引用和链接跳转都不会发送 referrer。

3、same-origin:对于同源的链接和引用,会发送referrer,其他的不会。

4、origin:在任何情况下仅发送源信息作为引用地址。源信息包括访问协议和域名。

5、strict-origin:在安全级别下降时不发送 referrer;而在同等安全级别的情况下仅发送源信息。注意:这个是新加的标准,有些浏览器可能还不支持。

6、origin-when-cross-origin:同源的链接和引用,会发送完全的 referrer 信息;但非同源链接和引用时,只发送源信息。

7、strict-origin-when-cross-origin:同源的链接和引用,会发送 referrer。安全级别下降时不发送 referrer。其它情况下发送源信息。注意:这个是新加的标准,有些浏览器可能还不支持。

8、unsafe-url:无论是否发生协议降级,无论是本站链接还是站外链接,统统都发送 Referrer 信息。正如其名,这是最宽松而最不安全的策略。

Referrer Policy 的设置方法

CSP(Content Security Policy

CSP(Content Security Policy),是一个跟页面内容安全有关的规范。在 HTTP 中通过响应头中的 Content-Security-Policy 字段来告诉浏览器当前页面要使用何种 CSP 策略。

 Content-Security-Policy: referrer no-referrer|no-referrer-when-downgrade|origin|origin-when-cross-origin|unsafe-url;

标签

<meta name="referrer" content="no-referrer|no-referrer-when-downgrade|origin|origin-when-crossorigin|unsafe-url">

如果 content 属性不是合法的取值,浏览器会自动选择 no-referrer 这种最严格的策略。

通过 a、area、link 元素的 referrer 属性

<a href="http://marksanders.cn/" referrer="no-referrer|origin|unsafe-url"></a>

解决方法

本站主页的“观影”和“书单”模块需要加载豆瓣网的少许电影海报图和图书封面图。由于豆瓣网图片服务器开启了防盗链,因此在主页设置了 no-referrer 标签。这样豆瓣图片服务器无法通过 Referer 判断来源,图片可正常显示,但这造成了百度统计无法正常工作。

如何在正常显示豆瓣网图片的同时,正常使用百度统计呢?

IMG 标签的 Referrer-Policy 属性

给引用外部图片资源的 <img> 标签增加 referrerpolicy 属性并将其值设为 no-referre。这样浏览器仅在该图片资源的请求时屏蔽 Referer 的值,而不影响网页的其他部分。去 MDN 了解更多有关 Referrer-Policy 属性

<img referrerpolicy="no-referrer" src="https://marksanders.cn/static/img/logo@2x.png" />

但 referrerpolicy 属性的兼容性并不高,在一些浏览器(包括 Safari)至今仍不兼容该属性。

referrerpolicy 属性的浏览器兼容性

使用 iframe 放置图片

另一种方法是将设置了防盗链的图片放在 iframe 框架中而不是直接放置于网页上。本站采用的便是这种方法。通过设置 iframe 标签的宽度和高度,以及 scrolling、frameborder 和 allowtransparency 属性可让用户无法轻易察觉该图片是嵌在 iframe 框架中,除非查看页面源代码。当然 iframe 中子页面的 padding 和 margin 也要设为 0。

当所放置图片的大小未知时,可能需要根据图片的实际大小动态调节 iframe 框架的长度和宽度。可参考如下 HTML 代码:

<iframe 
    style="border: none"
    scrolling="no"
    frameborder="no
    allowtransparency="true"
    id="*********"  <!-- 这里需要为 iframe 标签设置 ID -->
    src="javascript:'
        <!doctype html>
        <html>
        <head>
        <meta charset=\'utf-8\'>
        <style>*{margin: 0; padding: 0; border: 0}</style>
        <script>
         function resizeWindow() {
            var elems  = document.getElementsByTagName(\\\'*\\\'),
            width  = 0,
            height = 0,
            first  = document.body.firstChild,
            elem;
            if (first.offsetHeight && first.offsetWidth) {
                width = first.offsetWidth;
                height = first.offsetHeight;
            } else {
                for (var i in elems) {
                    elem = elems[i];
                    if (!elem.offsetWidth) {
                        continue;
                    }
                    width  = Math.max(elem.offsetWidth, width);
                    height = Math.max(elem.offsetHeight, height);
                }
            }
            var ifr = parent.document.getElementById(\'*********\');    <!-- 这里是 iframe 标签的 ID -->
            ifr.height = height;
            ifr.width  = width;
        }
        </script>
        </head>
        <body onload="resizeWindow()">
            <img src="***************" />    <!-- 这里是需要显示的图片 -->
        </body>
    </html>
    '"></iframe>

iframe 中的子页面在加载完 <img> 标签中的图片后,调用 resizeWindow() 方法,根据图片的实际大小调整父页面中 iframe 的长度和宽度。

本站主页运用此种方法,效果如下:

本站主页效果

本站主页代码

问题解决。

上一篇:计算机网络 学习笔记

1. 绪论 1.1 计算机网络在信息时代的作用 互联网基本特点: 连通性: 互联网上用户不管距离多远,都能通信,就像这些用户终端都彼此连通 共享性: 指资源共享,包含信息、软件、硬件等共享,就像资源在用户身边 计算机网络(网络)的组成: 由若干结点和连接这些结点的链路组成; 结点可以是计算机、集线器、交换机、路由器等 1.2 因特网概述 我们先给出关于网络,互联网,因特

下一篇:不要用 let's encrypt 作为 flutter 的服务端证书

在中国大陆,不要使用 let&#39;s encrypt 作为 flutter 应用的服务端证书。 起因 几天前更新了 triplan 的 1.2.3 版本,收到了大量 iOS 用户的反馈:卡顿太夸张。与此同时未收到 Android 用户有关卡顿的反馈。更奇怪的是,卡顿现象貌似只出现在发起网络请求之后。这里我使用的是网络请求库 dio。一开始以为是代码优化做的不够好,或者是服务器请求过多造成线

雁过留痕,风过留声

目 录